#include "../../core/graphic/render_item.h"
#include <pspdisplay.h>
#include <pspgu.h>
#include <pspgum.h>
#include <psputils.h>
#include <string.h>  // For memset function

static KRenderState mCurrentRS;
static int pirmTypeMap[] = {
	GU_TRIANGLE_STRIP,
	GU_TRIANGLES,
	GU_LINE_STRIP,
	GU_LINES
};
static unsigned int g_BGColor = 0;

struct PSP_HW_BUFFER
{
	void* pVB;
	int primType;
	int vertexFVF;
	unsigned int vertexCount;
	PSP_HW_BUFFER() {pVB = NULL; vertexCount = 0;}
};

/************************************************************************/
/*              PSP vertex format                                       */
/************************************************************************/
#define PSP_FVF_PC (GU_COLOR_8888|GU_VERTEX_32BITF|GU_TRANSFORM_3D)
#define PSP_FVF_PT (GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_3D)
#define PSP_FVF_P (GU_VERTEX_32BITF|GU_TRANSFORM_3D)

/************************************************************************/
/*                      End of PSP vertex format                        */
/************************************************************************/


void ApplyBlendState(const KRenderState& rs)
{
	switch (rs.mBlendMode) {
	case KRenderState::eDisableBlend:
		sceGuDisable(GU_BLEND);
		break;
	case KRenderState::eAlphaBlend:
		sceGuEnable(GU_BLEND);
		sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
		break;
	case KRenderState::eAddBlend:
		sceGuEnable(GU_BLEND);
		sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, 0xffffffff, 0xffffffff);
		break;
	}

}

void KDrawManager::SetRenderState(const KRenderState& rs)
{
	if (mCurrentRS.mBlendMode != rs.mBlendMode)
		ApplyBlendState(rs);

	sceGuAmbientColor(rs.mModulateColor);

	if (rs.mTexture) {
		sceGuEnable(GU_TEXTURE_2D);
		rs.mTexture->Bind();
	}
	else {
		sceGuDisable(GU_TEXTURE_2D);
	}
}

void SetWorldMatrix(const KDrawLocal& local)
{
	sceGumMatrixMode(GU_MODEL);
	ScePspFMatrix4 transMat;
	//memset(&transMat, 0, sizeof(ScePspFMatrix4));
	transMat.x.x = local.rot.mCosTheta;
	transMat.y.x = -local.rot.mSinTheta;
	transMat.z.x = 0;
	transMat.w.x = local.x;

	transMat.x.y = local.rot.mSinTheta;
	transMat.y.y = local.rot.mCosTheta;
	transMat.z.y = 0;
	transMat.w.y = local.y;

	transMat.x.z = 0;
	transMat.y.z = 0;
	transMat.z.z = 1;
	transMat.w.z = 0;

	transMat.x.w = 0;
	transMat.y.w = 0;
	transMat.z.w = 0;
	transMat.w.w = 1;

	sceGumLoadMatrix(&transMat);
}

void KDrawManager::Draw_P(const VERTEX_P*  pData, const KDrawLocal& local, KPrimType type, unsigned int vertCnt)
{
	VERTEX_P* pVert = (VERTEX_P*)sceGuGetMemory(vertCnt * sizeof(VERTEX_P));
	memcpy(pVert, pData, vertCnt * sizeof(VERTEX_P));
	SetWorldMatrix(local);
	sceGumDrawArray(pirmTypeMap[type], PSP_FVF_P, vertCnt, 0 ,pVert);
}

void KDrawManager::Draw_PC(const VERTEX_PC*  pData, const KDrawLocal& local, KPrimType type, unsigned int vertCnt)
{
	sceGuDisable(GU_TEXTURE_2D);
	sceGuAmbientColor(0xffffffff);
	VERTEX_PC* pVert = (VERTEX_PC*)sceGuGetMemory(vertCnt * sizeof(VERTEX_PC));
	memcpy(pVert, pData, vertCnt * sizeof(VERTEX_PC));
	SetWorldMatrix(local);
	sceGumDrawArray(pirmTypeMap[type], PSP_FVF_PC, vertCnt, 0 ,pVert);
}

void KDrawManager::Draw_PT(const VERTEX_PT*  pData, const KDrawLocal& local, KPrimType type, unsigned int vertCnt)
{
	VERTEX_PT* pVert = (VERTEX_PT*)sceGuGetMemory(vertCnt * sizeof(VERTEX_PT));
	memcpy(pVert, pData, vertCnt * sizeof(VERTEX_PT));
	SetWorldMatrix(local);
	sceGumDrawArray(pirmTypeMap[type], PSP_FVF_PT, vertCnt, 0 ,pVert);
}

void KDrawManager::Draw_HWBuf(const KDrawLocal& local, HW_BufferPtr pBuffer)
{
	PSP_HW_BUFFER* pPSPBuffer = (PSP_HW_BUFFER*)pBuffer;
	SetWorldMatrix(local);
	
	if (!(pPSPBuffer->vertexFVF & GU_TEXTURE_32BITF)) {
		sceGuAmbientColor(0xffffffff);
		sceGuDisable(GU_TEXTURE_2D);
	}
	
	sceGumDrawArray(pPSPBuffer->primType, pPSPBuffer->vertexFVF, pPSPBuffer->vertexCount, 0 ,pPSPBuffer->pVB);
}

HW_BufferPtr KDrawManager::CreateHWBuffer(const VERTEX_P*  pData, KPrimType type, unsigned int vertCnt)
{
	PSP_HW_BUFFER* pBuffer = new PSP_HW_BUFFER;

	pBuffer->primType = pirmTypeMap[type];
	pBuffer->vertexCount = vertCnt;
	pBuffer->vertexFVF = PSP_FVF_P;
	pBuffer->pVB = malloc(sizeof(VERTEX_P) * vertCnt);
	memcpy(pBuffer->pVB, pData, sizeof(VERTEX_P) * vertCnt);
	// Make the data is written back to memory(which will be accessed by GU)
	sceKernelDcacheWritebackInvalidateAll();
	return pBuffer;
}

HW_BufferPtr KDrawManager::CreateHWBuffer(const VERTEX_PC* pData, KPrimType type, unsigned int vertCnt)
{
	PSP_HW_BUFFER* pBuffer = new PSP_HW_BUFFER;

	pBuffer->primType = pirmTypeMap[type];
	pBuffer->vertexCount = vertCnt;
	pBuffer->vertexFVF = PSP_FVF_PC;
	pBuffer->pVB = malloc(sizeof(VERTEX_PC) * vertCnt);
	memcpy(pBuffer->pVB, pData, sizeof(VERTEX_PC) * vertCnt);
	// Make the data is written back to memory(which will be accessed by GU)
	sceKernelDcacheWritebackInvalidateAll();
	return pBuffer;
}

HW_BufferPtr KDrawManager::CreateHWBuffer(const VERTEX_PT* pData, KPrimType type, unsigned int vertCnt)
{
	PSP_HW_BUFFER* pBuffer = new PSP_HW_BUFFER;

	pBuffer->primType = pirmTypeMap[type];
	pBuffer->vertexCount = vertCnt;
	pBuffer->vertexFVF = PSP_FVF_PT;
	pBuffer->pVB = malloc(sizeof(VERTEX_PT) * vertCnt);
	memcpy(pBuffer->pVB, pData, sizeof(VERTEX_PT) * vertCnt);
	// Make the data is written back to memory(which will be accessed by GU)
	sceKernelDcacheWritebackInvalidateAll();
	return pBuffer;
}

void KDrawManager::DeleteHWBuffer(HW_BufferPtr pBuffer)
{
	PSP_HW_BUFFER* pPSPBuffer = (PSP_HW_BUFFER*)pBuffer;
	if (pPSPBuffer) free(pPSPBuffer->pVB);
	delete pPSPBuffer;
}

static KRenderState defaultRS;

void KDrawManager::Begin()
{	
	mCurrentRS = defaultRS;

	sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);

	sceGuEnable(GU_ALPHA_TEST);
	sceGuAlphaFunc(GU_GEQUAL, 0x00000001, 0xffffffff);

	sceGuDisable(GU_TEXTURE_2D);
	ApplyBlendState(mCurrentRS);

	sceGuAmbientColor(mCurrentRS.mModulateColor);

	sceGuClearColor(g_BGColor);
	sceGuClear(GU_COLOR_BUFFER_BIT);
}

void KDrawManager::End()
{
	sceGuFinish();
	sceGuSync(0,0);

	sceDisplayWaitVblankStart();
	sceGuSwapBuffers();
}

void KDrawManager::SetupView(float w, float h, const KVec2& center, KColor bgClr)
{
	g_BGColor = bgClr;
	sceGumMatrixMode(GU_PROJECTION);
	sceGumLoadIdentity();

	sceGumMatrixMode(GU_VIEW);
	sceGumLoadIdentity();

	sceGumOrtho(
		-w*0.5f, w*0.5f,
		-h*0.5f, h*0.5f,
		-1, 1);

	ScePspFVector3 pos = {-center[0], -center[1], 0};
	sceGumTranslate(&pos);
}


void KDrawManager::DrawSprite(const KDrawLocal& local, KTexture* pTexture, const KSpriteRect& rect, KColor color)
{
	sceGuEnable(GU_TEXTURE_2D);
	pTexture->Bind();

	float hx = (float)rect.w;
	float hy = (float)rect.h;
	float u0 = (float)rect.sx / (float)pTexture->mWidth;
	float u1 = (float)(rect.sx+rect.w) / (float)pTexture->mWidth;
	float v0 = (float)rect.sy / (float)pTexture->mHeight;
	float v1 = (float)(rect.sy+rect.h) / (float)pTexture->mHeight;

	sceGuAmbientColor(color);

	sceGumMatrixMode(GU_MODEL);
	ScePspFMatrix4 transMat;
	//memset(&transMat, 0, sizeof(ScePspFMatrix4));
	transMat.x.x = local.rot.mCosTheta;
	transMat.y.x = -local.rot.mSinTheta;
	transMat.z.x = 0;
	transMat.w.x = local.x;

	transMat.x.y = local.rot.mSinTheta;
	transMat.y.y = local.rot.mCosTheta;
	transMat.z.y = 0;
	transMat.w.y = local.y;
	
	transMat.x.z = 0;
	transMat.y.z = 0;
	transMat.z.z = 1;
	transMat.w.z = 0;

	transMat.x.w = 0;
	transMat.y.w = 0;
	transMat.z.w = 0;
	transMat.w.w = 1;
	sceGumLoadMatrix(&transMat);

	float drawData[] = {
		u0, v1, -rect.cx,   -rect.cy,   0,   
		u1, v1,	hx-rect.cx, -rect.cy,   0,   
		u0, v0,	-rect.cx, hy-rect.cy,   0,   
		u1, v0,	hx-rect.cx, hy-rect.cy, 0
	};
	VERTEX_PT* pVert = (VERTEX_PT*)sceGuGetMemory(4 * sizeof(VERTEX_PT));
	memcpy(pVert, drawData, sizeof(VERTEX_PT)*4);
	sceGumDrawArray(GU_TRIANGLE_STRIP, PSP_FVF_PT, 4, 0 ,pVert);
}

void KDrawManager::GetViewportSize(unsigned int& width, unsigned int& height)
{
	width = 480;
	height = 272;
}